﻿using System;
using System.Linq;
using System.Drawing;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections.Generic;
using System.Data.EntityClient;
using System.Windows.Forms;
using UtfprLibrary.EntityFramework.ComponentModel;
using UtfprLibrary.Windows.Forms;
using System.Data.SqlClient;
using System.IO;
using System.Reflection;
using WinFormApp_Sample.Data;

namespace WinFormApp_Sample.Data {
    [ToolboxItem(true), ToolboxBitmap(typeof(ResourceFinder), "Resources.APP_DataSourceWinFormApp.png")]
    public partial class EDMWinFormAppDataSource : Component, IEntityDataSourceProvider {
        #region Constructors

        public EDMWinFormAppDataSource() {
            // create entity data source
            _dataSource = new UtfprDataSource();

            // create object context (specifying the connection string)
            try {
                _dataSource.ObjectContext = new EDMWinFormApp(GetConnectionString());
            } catch (Exception ex) {
                throw new ArgumentException(ex.Message);
            }
        }

        public EDMWinFormAppDataSource(IContainer container)
            : this() {
            container.Add(this);
        }

        #endregion

        public EntitySetCollection ES {
            get {
                return _dataSource.EntitySets;
            }
        }

        #region Methods

        public void SaveChanges() {
            // any logic/validation you want
            // ...

            // save the changes if everything is OK
            _dataSource.SaveChanges();
        }
        
        private static string GetConnectionString() {
            try {
                string dbPath = new FileInfo(Assembly.GetExecutingAssembly().Location)
                   .DirectoryName + "\\App_Data\\WinFormAppDatabase.mdf";

                var providerCs = new SqlConnectionStringBuilder();
                providerCs.AttachDBFilename = dbPath;
                providerCs.DataSource = ".\\SQLEXPRESS";

                //-----
                //do not add InitialCatalog
                //providerCs.InitialCatalog = "EcovilleDatabase";
                //read this: http://www.skimedic.com/blog/post/2010/09/18/SQL-Express-Error-Database-3cfoo3e-already-exists-Choose-a-different-database-name.aspx
                //-----

                providerCs.UserInstance = true;
                providerCs.IntegratedSecurity = true;
                var csBuilder = new EntityConnectionStringBuilder();
                csBuilder.Provider = "System.Data.SqlClient";
                csBuilder.ProviderConnectionString = providerCs.ToString();
                csBuilder.Metadata = string.Format(
                    "res://{0}/EDMWinFormApp.EDMWinFormApp.csdl|res://{0}/EDMWinFormApp.EDMWinFormApp.ssdl|res://{0}/EDMWinFormApp.EDMWinFormApp.msl",
                    typeof(EDMWinFormApp).Assembly.FullName);
                return csBuilder.ToString();
            } catch (Exception ex) {
                throw ex;
            }
        }

        #endregion

        #region IEntityDataSourceProvider

        bool IListSource.ContainsListCollection {
            get { return true; }
        }

        System.Collections.IList IListSource.GetList() {
            return ((IListSource)_dataSource).GetList();

            //// This method is completely undocumented in MSDN.
            ////
            //// Instead of returning a list of the lists available (nice and logical),
            //// you should return a list with ONE object, which exposes the lists available 
            //// as if they were properties of this ONE object. UGH!
            ////
            //var list = new List<EntitySetTypeDescriptor>();
            //list.Add(new EntitySetTypeDescriptor(this._dataSource));

            //return list;
        }

        private ComponentCollection _bindingSources;
        [Category("Data"), Description("A IComponent Collection representing all BindingSources of the Form.")]
        public ComponentCollection BindingSources {
            get {
                var host = base.Site.GetService(typeof(IDesignerHost)) as IDesignerHost;
                if (host != null) {
                    if (host.RootComponent is ContainerControl) {
                        //get the binding sources
                        var bindingSources = (host.RootComponent as ContainerControl)
                            .Container.Components.OfType<BindingSource>();

                        if (_bindingSources == null || _bindingSources.Count != bindingSources.Count()) {
                            var ic = new IComponent[bindingSources.Count()];
                            int i = 0;
                            foreach (var bs in bindingSources) {
                                ic[i] = bs;
                                i++;
                            }
                            _bindingSources = new ComponentCollection(ic);
                        }
                    }
                }

                return _bindingSources;                
            }
        }

        private IEntityDataSource _dataSource;
        IEntityDataSource IEntityDataSourceProvider.EntityDataSource {
            get { return _dataSource; }
        }

        #endregion

        #region IComponentChangeService

        private IComponentChangeService _changeService;

        // This override allows the control to register event handlers for IComponentChangeService events
        // at the time the control is sited, which happens only in design mode.
        public override ISite Site {
            get { return base.Site; }
            set {
                // Clear any component change event handlers.
                clearChangeNotifications();

                // Set the new Site value.
                base.Site = value;

                _changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService));

                try {
                    var host = base.Site.GetService(typeof(IDesignerHost)) as IDesignerHost;
                    if (host != null) {
                        if (host.Container.Components["edmWinFormAppBindingSource1"] == null) {
                            var source = new BindingSource();
                            source.DataSource = this;
                            host.Container.Add(source, "edmWinFormAppBindingSource1");
                        } 
                    }
                } catch { }
                
                // Register event handlers for component change events.
                registerChangeNotifications();
            }
        }

        private void clearChangeNotifications() {
            // The m_changeService value is null when not in design mode, 
            // as the IComponentChangeService is only available at design time.	
            _changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService));

            // Clear our the component change events to prepare for re-siting.				
            if (_changeService != null) {
                _changeService.ComponentAdded -= new ComponentEventHandler(onComponentAdded);
            }
        }

        private void registerChangeNotifications() {
            // Register the event handlers for the IComponentChangeService events
            if (_changeService != null) {
                _changeService.ComponentAdded += new ComponentEventHandler(onComponentAdded);
            }
        }

        /* This method handles the OnComponentAdded event to display a notification. */
        private void onComponentAdded(object sender, ComponentEventArgs ce) {
            if (ce.Component is BindingSource) {
                var bs = (BindingSource)ce.Component;
                if (this._dataSource != null && bs.DataSource != null) {
                    foreach (var es in _dataSource.EntitySets) {
                        try {
                            if (es.ElementType == (Type)bs.DataSource) {
                                var host = base.Site.GetService(typeof(IDesignerHost)) as IDesignerHost;
                                if (host != null) {
                                    bs.DataSource = host.Container.Components["edmWinFormAppBindingSource1"];
                                }

                                //bs.DataSource = this;
                                bs.DataMember = es.Name;
                            }
                        } catch { }
                    }
                }
            } else if (ce.Component is IEntityDataSourceProvider) {
                var host = base.Site.GetService(typeof(IDesignerHost)) as IDesignerHost;
                if (host != null) {
                    if (host.RootComponent is ContainerControl) {
                        var containerControl = host.RootComponent as ContainerControl;
                        if (containerControl.Container.Components.OfType<IEntityDataSourceProvider>().Count() > 1) {
                            throw new ArgumentException("Cannot have multiple datasources.");
                        }

                        var bindingSources = containerControl
                            .Container.Components.OfType<BindingSource>();

                        foreach (var bs in bindingSources) {
                            if (_dataSource != null && bs.DataSource != null) {
                                foreach (var es in _dataSource.EntitySets) {
                                    try {
                                        if (es.ElementType == (Type)bs.DataSource) {
                                            bs.DataSource = this;
                                            bs.DataMember = es.Name;
                                        }
                                    } catch { }
                                }
                            }
                        }
                    }
                }
            }
        }

        #endregion       
    }
}